//
//  BookDao.swift
//  KoiAccounting
//
//  Created by 纪洪文 on 16.03.25.
//

import Foundation
import GRDB

class BookDao {
    let dbQueue: DatabaseQueue
    
    init(dbQueue: DatabaseQueue) {
        self.dbQueue = dbQueue
    }
    
    
    func save(book:AccountingBook?) {
        if book == nil {
            return
        }
        var book = book!
        if (book.bookId == nil || book.bookId!.isEmpty) {
            book.bookId = ShortUUID().generate()
        }
        let now = Date()
        let createTime = now.toISODateString()
        book.createTime = createTime
        book.updateTime = createTime
        let maxOrderId = (try? fetchMaxOrderId()) ?? 0
        book.orderId = maxOrderId + 1
        do {
            try dbQueue.write { db in
                try book.insert(db)
            }
        } catch {
            logger.error("save book fail \(error)")
        }
        // 保存账本后，应该创建默认类别
        CategoryManager.standard.createDefaultCategorys(uid: book.uid.orEmpty, bookId: book.bookId.orEmpty)
    }
    
    func update(book: AccountingBook) {
        var book = book
        book.makeUpdate()
        updateDB(book: book)
    }
    
    func update(books: Array<AccountingBook>) {
        do {
            try dbQueue.write { db in
                for book in books {
                    var book = book
                    book.makeUpdate()
                    try book.update(db)
                }
            }
        } catch {
            logger.error("update books fail \(error)")
        }
    }
    
    func delete(book: AccountingBook) {
        var book = book
        book.makeDelete()
        updateDB(book: book)
    }
    
    func archive(book: AccountingBook) {
        var book = book
        book.archive()
        updateDB(book: book)
    }
    
    func unArchive(book: AccountingBook) {
        var book = book
        book.unArchive()
        updateDB(book: book)
    }
    
    private func updateDB(book: AccountingBook) {
        do {
            try dbQueue.write { db in
                try book.update(db)
            }
        } catch {
            logger.error("update book fail \(error)")
        }
    }
    
    // 查询最大的 orderId
    func fetchMaxOrderId() throws -> Int? {
        return try dbQueue.read { db in
            try Int.fetchOne(db, sql: "SELECT MAX(order_id) FROM accounting_book")
        }
    }
    
    func getBook(uid: String, bookId: String) -> AccountingBook? {
        do {
            let book = try dbQueue.read { db in
                try AccountingBook.filter(AccountingBook.Columns.uid == uid).filter(AccountingBook.Columns.bookId == bookId).fetchOne(db)
            }
            return book
        } catch {
            return nil
        }
    }
    
    func getAllBook(uid: String) -> Array<AccountingBook> {
        do {
            return try dbQueue.read{ db in
                try AccountingBook
                    .filter(AccountingBook.Columns.uid == uid)
                    .filter(AccountingBook.Columns.isDel != 1)
                    .filter(AccountingBook.Columns.isArchive != 1)
                    .order(AccountingBook.Columns.orderId.asc)
                    .fetchAll(db)
            }
        } catch {
            return Array<AccountingBook>()
        }
    }
    
    func getArchiveBooks(uid: String) -> Array<AccountingBook>{
        do {
            return try dbQueue.read { db in
                try AccountingBook
                    .filter(AccountingBook.Columns.uid == uid)
                    .filter(AccountingBook.Columns.isDel != 1)
                    .filter(AccountingBook.Columns.isArchive == 1)
                    .order(AccountingBook.Columns.updateTime.desc)
                    .fetchAll(db)
            }
        } catch {
            return Array<AccountingBook>()
        }
    }
    
    func deleteBook(uid:String, bookId:String) {
        do {
            _ = try dbQueue.write { db in
                 try AccountingBook.deleteOne(db, key: ["uid":uid,"book_id":bookId])
            }
        } catch {
            logger.error("deleteBook fail \(error)")
        }
    }
    
    
    func getPushData(pageSize:Int, offset:Int, uid: String) -> Array<AccountingBook> {
        do {
            return try dbQueue.read { db in
                try AccountingBook
                    .filter(AccountingBook.Columns.uid == uid)
                    .filter(AccountingBook.Columns.dataFlag != DataFlag.synced.rawValue)
                    .limit(pageSize, offset: offset)
                    .fetchAll(db)
            }
            
        } catch {
            logger.error("getPushData fail \(error)")
        }
        return [AccountingBook]()
    }
    
    func updateSync(books: Array<AccountingBook>) throws {
        try dbQueue.write { db in
            for book in books {
                var book = book
                try book.insert(db, onConflict: .replace)
            }
        }
    }
}
